/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package co.edu.ecci.simulador.icetex.negocio;

import co.edu.ecci.simulador.icetex.vo.VoAmortizacion;
import co.edu.ecci.simulador.icetex.vo.VoDatosEntrada;
import co.edu.ecci.simulador.icetex.vo.VoPrestamo;
import co.edu.ecci.simulador.icetex.vo.VoResultado;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.ResourceBundle;

/**
 *
 * @author JuanDa
 */
public class NSimulador {

    private ResourceBundle props = ResourceBundle.getBundle("co.edu.ecci.simulador.icetex.resources.data");
    private BigDecimal tasa_interes_simple = BigDecimal.ZERO;
    private BigDecimal tasa_interes_compuesto = BigDecimal.ZERO;
    private BigDecimal incremento_anual = BigDecimal.ZERO;
    private BigDecimal ipc = BigDecimal.ZERO;
    private BigDecimal incremento_ipc = BigDecimal.ZERO;
    private BigDecimal porcentaje_est_alto = BigDecimal.ZERO;
    private BigDecimal porcentaje_est_bajo = BigDecimal.ZERO;
    private Calendar fechaFocal;
    private int dias_anio = 365;
    private int periodos = 0;
    private static final int MILISEGUNDOS_POR_DIA = 24 * 60 * 60 * 1000;

    public VoResultado obtenerCotizacion(VoDatosEntrada datos) throws Exception {
        inicializarParametros();
        VoResultado resultado = new VoResultado();

        if (datos.getEstrato() > 0 && datos.getEstrato() < 3) {
            tasa_interes_compuesto = ipc;
        } else if (datos.getEstrato() > 2 && datos.getEstrato() < 7) {
            tasa_interes_compuesto = ipc.add(incremento_ipc);
        }

        tasa_interes_compuesto = new BigDecimal(Math.pow(tasa_interes_compuesto.doubleValue() + 1.0, 1.0 / 12.0) - 1.0);

        List<VoPrestamo> listadoPrestamo = calcularTablaPrestamo(datos);

        BigDecimal totalPrestado = BigDecimal.ZERO;
        BigDecimal totalInt = BigDecimal.ZERO;
        BigDecimal totalPrestadoInt = BigDecimal.ZERO;
        for (VoPrestamo voPrestamo : listadoPrestamo) {
            totalPrestado = totalPrestado.add(voPrestamo.getValorDesembolso());
            totalInt = totalInt.add(voPrestamo.getValorInteres());
            totalPrestadoInt = totalPrestadoInt.add(voPrestamo.getValorDesembolsoInteres());
        }
        resultado.setValorPrestadoInicial(listadoPrestamo.get(0).getValorDesembolso());
        resultado.setValorPrestado(totalPrestado);
        resultado.setValorInterePrestado(totalInt);
        resultado.setValorPrestadoMasIntereses(totalPrestadoInt);

        resultado.setListaPrestamo(listadoPrestamo);
        resultado.setValorCuota(calcularCuota(resultado.getValorPrestadoMasIntereses()));
        resultado.setListaAmortizacion(calcularTablaAmortizacion(datos, resultado.getValorPrestadoMasIntereses(), resultado.getValorCuota(), periodos * 6 * 2));
        resultado.setNumeroCuotas(resultado.getListaAmortizacion().size());

        BigDecimal interesesAmortizacion = BigDecimal.ZERO;
        for (VoAmortizacion amortizacion : resultado.getListaAmortizacion()) {
            interesesAmortizacion = interesesAmortizacion.add(amortizacion.getIntereses());
        }
        resultado.setValorInteresesAmortizacion(interesesAmortizacion);

        resultado.setValorTotalPagado(resultado.getValorCuota().multiply(new BigDecimal(resultado.getNumeroCuotas())));

        return resultado;
    }

    private List<VoPrestamo> calcularTablaPrestamo(VoDatosEntrada datos) throws Exception {

        BigDecimal valorPrestado;
        if (datos.getEstrato() > 0 && datos.getEstrato() < 3) {
            valorPrestado = datos.getMatricula().multiply(porcentaje_est_bajo);
        } else if (datos.getEstrato() > 2 && datos.getEstrato() < 7) {
            valorPrestado = datos.getMatricula().multiply(porcentaje_est_alto);
        } else {
            throw new Exception(props.getString("estrato_fuera_rango"));
        }

        periodos = datos.getNroSemestreFinal() - datos.getNroSemestreIngreso() + 1;

        Calendar fechaIngreso = Calendar.getInstance();
        fechaIngreso.setTime(datos.getFechaIngreso());

        fechaFocal = Calendar.getInstance();
        fechaFocal.setTime(datos.getFechaIngreso());
        fechaFocal.set(Calendar.MONTH, fechaFocal.get(Calendar.MONTH) + (periodos + 2) * 6);

        Calendar fechaIniciaPeriodo = fechaIngreso;

        //Indica si inicia en el 1er o 2do semestre del año.
        int corteInicial = fechaIngreso.get(Calendar.MONTH) < 7 ? 1 : 2;

        BigDecimal vlActualMatricula = valorPrestado;

        List<VoPrestamo> listadoPrestamo = new ArrayList<>();

        for (int i = datos.getNroSemestreIngreso() - 1; i < datos.getNroSemestreFinal(); i++) {
            boolean cambiaAnio;
            cambiaAnio = corteInicial == 1 ? i % 2 == 0 : i % 2 == 1;

            VoPrestamo prestamo = new VoPrestamo();
            if (cambiaAnio && i > 0) {
                vlActualMatricula = vlActualMatricula.multiply(incremento_anual.add(BigDecimal.ONE));
            }
            prestamo.setSemestre(i + 1);
            prestamo.setFecha(fechaIniciaPeriodo.getTime());
            prestamo.setValorDesembolso(vlActualMatricula);
            prestamo.setDiasFF(new Long((fechaFocal.getTimeInMillis() - fechaIniciaPeriodo.getTimeInMillis()) / MILISEGUNDOS_POR_DIA).intValue());
            prestamo.setValorInteres(vlActualMatricula.multiply(tasa_interes_simple, MathContext.UNLIMITED).multiply(new BigDecimal(prestamo.getDiasFF()), MathContext.UNLIMITED).divide(new BigDecimal(dias_anio, MathContext.UNLIMITED), BigDecimal.ROUND_UP));
            prestamo.setValorDesembolsoInteres(prestamo.getValorDesembolso().add(prestamo.getValorInteres()));

            listadoPrestamo.add(prestamo);

            fechaIniciaPeriodo.set(Calendar.MONTH, fechaIniciaPeriodo.get(Calendar.MONTH) + 6);
        }
        return listadoPrestamo;
    }

    private List<VoAmortizacion> calcularTablaAmortizacion(VoDatosEntrada datos, BigDecimal totalPrestado, BigDecimal cuota, int periodosPago) throws Exception {
        List<VoAmortizacion> tablaAmortizacion = new ArrayList<>();

        BigDecimal valorSaldo = totalPrestado;
        for (int i = 1; i <= periodosPago; i++) {
            VoAmortizacion amortizacion = new VoAmortizacion();
            amortizacion.setPeriodo(i);
            amortizacion.setCapital(valorSaldo);
            amortizacion.setIntereses(valorSaldo.multiply(tasa_interes_compuesto));
            amortizacion.setCuota(cuota);
            amortizacion.setAmortizacion(amortizacion.getCuota().subtract(amortizacion.getIntereses(), MathContext.UNLIMITED));
            amortizacion.setSaldo(amortizacion.getCapital().subtract(amortizacion.getAmortizacion(), MathContext.UNLIMITED));
            valorSaldo = amortizacion.getSaldo();

            tablaAmortizacion.add(amortizacion);
        }
        return tablaAmortizacion;
    }

    private BigDecimal calcularCuota(BigDecimal totalPrestado) {
        int exp = periodos * 6 * 2 * -1;
        BigDecimal tasaCalculada = BigDecimal.ONE.add(tasa_interes_compuesto, MathContext.UNLIMITED);
        BigDecimal tasaExp = tasaCalculada.pow(exp, MathContext.DECIMAL128);
        BigDecimal tasaMeses = BigDecimal.ONE.subtract(tasaExp, MathContext.UNLIMITED);
        BigDecimal tasaMesesTasa = tasaMeses.divide(new BigDecimal(tasa_interes_compuesto.doubleValue(), MathContext.UNLIMITED), RoundingMode.UP);
        BigDecimal cuota = totalPrestado.divide(tasaMesesTasa, BigDecimal.ROUND_UP);
        return cuota;
    }

    private void inicializarParametros() {
        porcentaje_est_bajo = new BigDecimal(props.getString("porcentaje_est_b"));
        porcentaje_est_alto = new BigDecimal(props.getString("porcentaje_est_a"));
        tasa_interes_simple = new BigDecimal(props.getString("tasa_interes_simple"));
        incremento_anual = new BigDecimal(props.getString("incremento_anual"));
        dias_anio = Integer.valueOf(props.getString("dias_anio"));
        ipc = new BigDecimal(props.getString("ipc"));
        incremento_ipc = new BigDecimal(props.getString("incremento_ipc"));
    }
}
